Responsive UI in Flutter
Responsive UI in Flutter means designing an application so that its interface automatically adjusts to the available screen space. A responsive Flutter application should work comfortably across phones, tablets, desktops, web browsers, foldable devices, and resizable windows.
Flutter distinguishes between responsive design, which focuses on fitting UI elements into available space, and adaptive design, which focuses on making the UI usable in that space. In practical Flutter development, both concepts are commonly used together. Flutter's official adaptive and responsive design guide explains this approach in detail.
1. Why Responsive UI Is Important
Users can access the same application from devices with very different screen sizes. A layout that looks good on a mobile phone may become too stretched, crowded, or difficult to use on a tablet or desktop.
Responsive design helps an application:
- Use available screen space effectively.
- Prevent content from overflowing.
- Improve readability.
- Support portrait and landscape layouts.
- Provide better tablet and desktop experiences.
- Support resizable application windows.
- Make navigation appropriate for the available width.
- Improve accessibility and usability.
2. Responsive vs Adaptive UI
| Responsive UI | Adaptive UI |
|---|
| Adjusts the layout to available space. | Changes the UI structure or interaction model when appropriate. |
| May resize, wrap, or reposition widgets. | May switch between different navigation patterns. |
| Focuses on fitting content. | Focuses on making the interface usable. |
| Example: changing grid columns. | Example: changing bottom navigation to a navigation rail. |
3. Common Responsive Design Strategies
- Use flexible widgets such as
Expanded and Flexible.
- Use
LayoutBuilder to respond to local layout constraints.
- Use
MediaQuery.sizeOf() when the decision depends on the application window size.
- Use
SafeArea to avoid system UI and display cutouts.
- Use
Wrap for content that should move onto another line.
- Use responsive grids.
- Use maximum content widths on large screens.
- Use different navigation layouts at appropriate breakpoints.
- Avoid hard-coding a single device width.
- Test the application at different window sizes.
4. Understanding Logical Pixels
Flutter layouts work with logical pixels rather than directly using physical screen pixels. This allows UI elements to maintain a more consistent visual size across different displays.
Container(
width: 200,
height: 100,
child: const Text('Responsive Box'),
)
However, fixed dimensions should be used carefully. A fixed width may work on one screen and create overflow or excessive empty space on another.
5. Using Expanded for Responsive Width
Expanded allows a child of a Row or Column to occupy available remaining space.
Row(
children: [
Expanded(
child: Container(
height: 100,
child: const Center(
child: Text('Left'),
),
),
),
Expanded(
child: Container(
height: 100,
child: const Center(
child: Text('Right'),
),
),
),
],
)
Both containers share the available horizontal space instead of using fixed widths.
6. Using Flexible
Flexible is useful when a widget should be allowed to shrink or use available space without necessarily forcing it to fill all remaining space.
Row(
children: [
Flexible(
child: Text(
'This is a long piece of text that can adapt to available width.',
),
),
const Icon(Icons.info),
],
)
7. Using MediaQuery.sizeOf()
MediaQuery.sizeOf(context) provides the current application window size. Flutter's current adaptive-design guidance recommends using MediaQuery.sizeOf when a responsive decision should be based on the overall app window. Flutter's general adaptive approach explains when to use MediaQuery.sizeOf and when to use LayoutBuilder.
final size = MediaQuery.sizeOf(context);
final width = size.width;
final height = size.height;
Example
class ResponsiveScreen extends StatelessWidget {
const ResponsiveScreen({super.key});
@override
Widget build(BuildContext context) {
final width = MediaQuery.sizeOf(context).width;
return Scaffold(
body: Center(
child: Text(
'Width: $width',
style: const TextStyle(fontSize: 24),
),
),
);
}
}
8. Why Use LayoutBuilder?
LayoutBuilder provides the constraints available to the widget from its parent. This makes it useful when a particular widget needs to adapt according to the space assigned to it rather than the entire application window.
LayoutBuilder(
builder: (context, constraints) {
return Text(
'Available width: ${constraints.maxWidth}',
);
},
)
The constraints object contains values such as:
minWidth
maxWidth
minHeight
maxHeight
9. Responsive Layout with LayoutBuilder
class ResponsiveHome extends StatelessWidget {
const ResponsiveHome({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth < 600) {
return const MobileLayout();
}
return const TabletLayout();
},
),
);
}
}
class MobileLayout extends StatelessWidget {
const MobileLayout({super.key});
@override
Widget build(BuildContext context) {
return const Center(
child: Text('Mobile Layout'),
);
}
}
class TabletLayout extends StatelessWidget {
const TabletLayout({super.key});
@override
Widget build(BuildContext context) {
return const Center(
child: Text('Tablet Layout'),
);
}
}
The important principle is that the layout is selected based on available width rather than simply assuming that a particular device type always has a particular size.
10. Responsive Breakpoints
A breakpoint is a width at which the application changes its layout strategy.
For example:
| Available Width | Possible Layout |
|---|
| Less than 600 | Compact mobile layout |
| 600 to 1023 | Tablet or medium layout |
| 1024 and above | Large tablet/desktop layout |
These numbers are examples for a project, not universal device rules. Flutter's adaptive guidance recommends choosing breakpoints based on when your UI needs to change rather than identifying a device as a phone or tablet.
11. Creating a Breakpoint Helper
class AppBreakpoints {
static const double mobile = 600;
static const double tablet = 1024;
}
Use it in a responsive widget:
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth < AppBreakpoints.mobile) {
return const MobileLayout();
}
if (constraints.maxWidth < AppBreakpoints.tablet) {
return const TabletLayout();
}
return const DesktopLayout();
},
)
12. Responsive Row and Column
A common responsive technique is to display content horizontally on large screens and vertically on small screens.
LayoutBuilder(
builder: (context, constraints) {
final isWide = constraints.maxWidth >= 700;
return Flex(
direction: isWide
? Axis.horizontal
: Axis.vertical,
children: [
Expanded(
child: Container(
padding: const EdgeInsets.all(20),
child: const Text('Section 1'),
),
),
Expanded(
child: Container(
padding: const EdgeInsets.all(20),
child: const Text('Section 2'),
),
),
],
);
},
)
13. Responsive GridView
Grid layouts are frequently used for dashboards, product listings, image galleries, and cards.
LayoutBuilder(
builder: (context, constraints) {
int columns;
if (constraints.maxWidth < 600) {
columns = 2;
} else if (constraints.maxWidth < 1000) {
columns = 3;
} else {
columns = 4;
}
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: columns,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
),
itemCount: 20,
itemBuilder: (context, index) {
return Card(
child: Center(
child: Text('Item ${index + 1}'),
),
);
},
);
},
)
14. Responsive Product Grid
class Product {
final String name;
final double price;
Product({
required this.name,
required this.price,
});
}
final products = [
Product(name: 'Laptop', price: 65000),
Product(name: 'Phone', price: 30000),
Product(name: 'Tablet', price: 25000),
Product(name: 'Watch', price: 5000),
Product(name: 'Camera', price: 45000),
Product(name: 'Headphones', price: 2500),
];
LayoutBuilder(
builder: (context, constraints) {
int columns = 2;
if (constraints.maxWidth >= 600) {
columns = 3;
}
if (constraints.maxWidth >= 1000) {
columns = 4;
}
return GridView.builder(
padding: const EdgeInsets.all(16),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: columns,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
childAspectRatio: 1.2,
),
itemCount: products.length,
itemBuilder: (context, index) {
final product = products[index];
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.shopping_bag,
size: 40,
),
const SizedBox(height: 10),
Text(
product.name,
textAlign: TextAlign.center,
),
const SizedBox(height: 5),
Text('₹${product.price}'),
],
),
),
);
},
);
},
)
15. Responsive Navigation
Navigation is one of the most important areas of responsive UI. A small window may use a bottom navigation bar, while a wider window can use a navigation rail or side navigation.
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth < 600) {
return Scaffold(
body: const Center(
child: Text('Mobile Content'),
),
bottomNavigationBar: NavigationBar(
destinations: const [
NavigationDestination(
icon: Icon(Icons.home),
label: 'Home',
),
NavigationDestination(
icon: Icon(Icons.person),
label: 'Profile',
),
],
),
);
}
return Scaffold(
body: Row(
children: [
NavigationRail(
destinations: const [
NavigationRailDestination(
icon: Icon(Icons.home),
label: Text('Home'),
),
NavigationRailDestination(
icon: Icon(Icons.person),
label: Text('Profile'),
),
],
selectedIndex: 0,
),
const Expanded(
child: Center(
child: Text('Large Screen Content'),
),
),
],
),
);
},
)
Flutter's adaptive design guidance uses this type of approach: navigation can change according to available window width rather than assuming a particular hardware category.
16. Responsive Sidebar Layout
A common desktop design uses a sidebar and a main content area. On a small screen, the sidebar can be replaced by another navigation pattern.
LayoutBuilder(
builder: (context, constraints) {
final isLarge = constraints.maxWidth >= 800;
if (isLarge) {
return Row(
children: [
SizedBox(
width: 250,
child: Container(
padding: const EdgeInsets.all(20),
child: const Text('Sidebar'),
),
),
const Expanded(
child: Center(
child: Text('Main Content'),
),
),
],
);
}
return const Center(
child: Text('Mobile Content'),
);
},
)
Flutter's adaptive-layout learning material demonstrates a similar large-screen pattern using a fixed-width sidebar and an Expanded detail area.
17. Responsive Dashboard
A dashboard can change the number of columns based on available width.
class Dashboard extends StatelessWidget {
const Dashboard({super.key});
@override
Widget build(BuildContext context) {
final items = [
('Users', '12,450', Icons.people),
('Orders', '3,240', Icons.shopping_cart),
('Revenue', '₹8,45,000', Icons.currency_rupee),
('Pending', '128', Icons.pending),
];
return LayoutBuilder(
builder: (context, constraints) {
final columns = constraints.maxWidth < 600
? 1
: constraints.maxWidth < 1000
? 2
: 4;
return GridView.builder(
padding: const EdgeInsets.all(16),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: columns,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
childAspectRatio: 1.5,
),
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
return Card(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(item.$3, size: 35),
const SizedBox(height: 10),
Text(item.$1),
Text(
item.$2,
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
),
),
],
),
);
},
);
},
);
}
}
18. Using Wrap for Responsive Content
Wrap automatically moves children to another line when there is not enough horizontal space.
Wrap(
spacing: 10,
runSpacing: 10,
children: [
Chip(label: Text('Flutter')),
Chip(label: Text('Dart')),
Chip(label: Text('Firebase')),
Chip(label: Text('UI Design')),
Chip(label: Text('API')),
Chip(label: Text('Database')),
],
)
This is useful for tags, filters, buttons, categories, and other content with variable widths.
19. Responsive Text
Text should be designed to work across different widths. Avoid forcing long text into a small fixed-width container.
Expanded(
child: Text(
'Flutter makes it possible to build responsive applications from a single codebase.',
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
)
20. Responsive Font Size
Font sizes can be adjusted when there is a genuine design requirement, but excessive device-specific scaling should be avoided.
LayoutBuilder(
builder: (context, constraints) {
final titleSize = constraints.maxWidth < 600
? 24.0
: 32.0;
return Text(
'Responsive Heading',
style: TextStyle(
fontSize: titleSize,
fontWeight: FontWeight.bold,
),
);
},
)
21. Responsive Padding
Large screens often benefit from additional horizontal margins, while smaller screens need compact spacing.
LayoutBuilder(
builder: (context, constraints) {
final padding = constraints.maxWidth < 600
? 16.0
: 32.0;
return Padding(
padding: EdgeInsets.symmetric(
horizontal: padding,
),
child: const Text(
'Responsive content area',
),
);
},
)
22. Limiting Content Width on Large Screens
Using the entire width of a large desktop screen for text can make reading uncomfortable. A maximum content width can provide a better reading area.
Center(
child: ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 900,
),
child: const Padding(
padding: EdgeInsets.all(24),
child: Text(
'This content remains within a readable maximum width.',
),
),
),
)
23. Using ConstrainedBox
ConstrainedBox allows developers to control the minimum and maximum dimensions of a widget.
ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 500,
),
child: TextField(
decoration: const InputDecoration(
labelText: 'Email',
border: OutlineInputBorder(),
),
),
)
24. Responsive Forms
Forms should not become extremely wide on desktop screens.
Center(
child: ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 500,
),
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
TextField(
decoration: const InputDecoration(
labelText: 'Name',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
TextField(
decoration: const InputDecoration(
labelText: 'Email',
border: OutlineInputBorder(),
),
),
],
),
),
),
)
25. Responsive Two-Column Form
LayoutBuilder(
builder: (context, constraints) {
final isWide = constraints.maxWidth >= 700;
if (isWide) {
return Row(
children: [
Expanded(
child: TextField(
decoration: const InputDecoration(
labelText: 'First Name',
border: OutlineInputBorder(),
),
),
),
const SizedBox(width: 16),
Expanded(
child: TextField(
decoration: const InputDecoration(
labelText: 'Last Name',
border: OutlineInputBorder(),
),
),
),
],
);
}
return Column(
children: [
TextField(
decoration: const InputDecoration(
labelText: 'First Name',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
TextField(
decoration: const InputDecoration(
labelText: 'Last Name',
border: OutlineInputBorder(),
),
),
],
);
},
)
26. SafeArea
SafeArea helps keep important content away from system UI, display cutouts, and areas such as status bars. Flutter's official documentation recommends using it around content that should not be obscured by these areas. Learn about SafeArea and MediaQuery.
Scaffold(
body: SafeArea(
child: Column(
children: [
const Text('Header'),
Expanded(
child: ListView(
children: const [
ListTile(title: Text('Item 1')),
ListTile(title: Text('Item 2')),
],
),
),
],
),
),
)
27. Responsive Images
Images should adapt to the available space instead of relying on fixed dimensions.
AspectRatio(
aspectRatio: 16 / 9,
child: Image.network(
'https://example.com/image.jpg',
fit: BoxFit.cover,
),
)
For responsive images inside a row:
Expanded(
child: Image.network(
'https://example.com/product.jpg',
fit: BoxFit.cover,
),
)
28. Responsive Dialogs
Dialogs can use a maximum width so that they do not become unnecessarily wide on desktop screens.
showDialog(
context: context,
builder: (context) {
return Dialog(
child: ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 500,
),
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'Responsive Dialog',
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 20),
const Text(
'This dialog has a maximum width.',
),
],
),
),
),
);
},
)
29. Responsive List and Detail Layout
A common adaptive application pattern is to display a list and details side-by-side on large screens while showing one screen at a time on smaller screens.
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth >= 800) {
return Row(
children: [
SizedBox(
width: 300,
child: UserList(),
),
const VerticalDivider(width: 1),
const Expanded(
child: UserDetails(),
),
],
);
}
return const UserList();
},
)
This pattern is useful for email applications, contact applications, messaging applications, administration systems, and document managers.
30. Orientation and Responsive Design
Applications can be displayed in portrait or landscape mode, but layout decisions should generally be based on available space rather than assuming that portrait always means phone and landscape always means tablet or desktop.
Flutter's adaptive-design guidance recommends using window size or local constraints when deciding how the UI should be structured. Flutter adaptive design best practices discusses why device type and orientation are not reliable substitutes for available layout space.
31. Responsive Layout with OrientationBuilder
OrientationBuilder can still be useful when the actual orientation is specifically relevant to the design.
OrientationBuilder(
builder: (context, orientation) {
return GridView.count(
crossAxisCount:
orientation == Orientation.portrait ? 2 : 4,
children: const [
Card(child: Center(child: Text('1'))),
Card(child: Center(child: Text('2'))),
Card(child: Center(child: Text('3'))),
Card(child: Center(child: Text('4'))),
],
);
},
)
For general adaptive layout decisions, prefer available window size or parent constraints rather than using orientation alone.
32. Responsive AppBar
LayoutBuilder(
builder: (context, constraints) {
final isLarge = constraints.maxWidth >= 700;
return AppBar(
title: const Text('My Application'),
actions: [
if (isLarge) ...[
TextButton(
onPressed: () {},
child: const Text('Home'),
),
TextButton(
onPressed: () {},
child: const Text('About'),
),
],
IconButton(
onPressed: () {},
icon: const Icon(Icons.person),
),
],
);
},
)
33. Responsive Visibility
Sometimes different widgets should appear at different widths.
LayoutBuilder(
builder: (context, constraints) {
final isMobile = constraints.maxWidth < 600;
return Column(
children: [
const Text('Common Header'),
if (isMobile)
const Text('Mobile Actions')
else
const Text('Desktop Actions'),
],
);
},
)
34. Responsive Spacing
Spacing can also be adapted to available space.
LayoutBuilder(
builder: (context, constraints) {
final spacing = constraints.maxWidth < 600
? 12.0
: 24.0;
return Column(
children: [
const Text('First Section'),
SizedBox(height: spacing),
const Text('Second Section'),
],
);
},
)
35. Building a Complete Responsive Screen
import 'package:flutter/material.dart';
class ResponsiveHome extends StatelessWidget {
const ResponsiveHome({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Responsive Dashboard'),
),
body: SafeArea(
child: LayoutBuilder(
builder: (context, constraints) {
final width = constraints.maxWidth;
int columns;
if (width < 600) {
columns = 1;
} else if (width < 1000) {
columns = 2;
} else {
columns = 4;
}
return Center(
child: ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 1400,
),
child: GridView.builder(
padding: const EdgeInsets.all(16),
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: columns,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
childAspectRatio: 1.5,
),
itemCount: 8,
itemBuilder: (context, index) {
return Card(
child: Center(
child: Text(
'Dashboard Card ${index + 1}',
textAlign: TextAlign.center,
),
),
);
},
),
),
);
},
),
),
);
}
}
36. How the Complete Example Works
Scaffold provides the basic application structure.
SafeArea protects content from system UI.
LayoutBuilder measures the available width.
- The number of grid columns changes according to available width.
ConstrainedBox prevents the dashboard from becoming excessively wide.
GridView.builder creates dashboard cards dynamically.
- The same code can respond to different window sizes without creating separate applications.
37. Avoiding Overflow
One of the most common problems in Flutter responsive layouts is the RenderFlex overflowed error.
Problem
Row(
children: [
const Text(
'This is a very long text that may overflow',
),
const Icon(Icons.info),
],
)
Better Solution
Row(
children: [
const Expanded(
child: Text(
'This is a very long text that can use available space.',
),
),
const Icon(Icons.info),
],
)
38. Common Responsive UI Mistakes
- Using fixed widths everywhere.
- Assuming every phone has the same screen width.
- Assuming landscape always means tablet or desktop.
- Using device type instead of available window size for layout decisions.
- Forgetting
SafeArea when content may be obscured.
- Allowing text to overflow.
- Making desktop forms unnecessarily wide.
- Using too many columns on small screens.
- Using very few columns on large screens.
- Ignoring keyboard, mouse, and touch interaction requirements.
- Not testing resizable windows.
39. Responsive UI Best Practices
- Design for available space rather than a specific device name.
- Use
LayoutBuilder when a widget needs to respond to its parent's constraints.
- Use
MediaQuery.sizeOf() when the layout decision depends on the overall application window.
- Use
Expanded and Flexible for flexible layouts.
- Use
Wrap for variable-width content.
- Use
ConstrainedBox or maximum widths for large-screen content.
- Use responsive grid column counts.
- Use appropriate navigation patterns for different window sizes.
- Use
SafeArea where content must avoid system UI.
- Keep widgets small and reusable.
- Test different widths, heights, orientations, and window configurations.
- Support touch, mouse, keyboard, and trackpad interactions where appropriate.
- Do not lock orientation simply to avoid handling responsive layouts.
40. Testing Responsive UI
Responsive interfaces should be tested at multiple sizes rather than only on one physical device.
Testing Checklist
- Small mobile width
- Large mobile width
- Portrait orientation
- Landscape orientation
- Tablet-sized window
- Desktop-sized window
- Resizable browser window
- Large monitor
- Different text scaling settings
- Keyboard and mouse input
- Touch interaction
- Long text and localized content
41. Responsive UI Architecture
A maintainable responsive application can separate common data and widgets from layout-specific arrangements.
lib/
├── main.dart
├── models/
│ └── product.dart
├── screens/
│ └── home_screen.dart
├── widgets/
│ ├── product_card.dart
│ ├── mobile_layout.dart
│ ├── tablet_layout.dart
│ └── desktop_layout.dart
└── utils/
└── breakpoints.dart
The same data and reusable components can then be used in multiple layouts.
42. Responsive Design Example: Mobile, Tablet, Desktop
class AdaptiveHome extends StatelessWidget {
const AdaptiveHome({super.key});
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
final width = constraints.maxWidth;
if (width < 600) {
return const MobileHome();
}
if (width < 1024) {
return const TabletHome();
}
return const DesktopHome();
},
);
}
}
class MobileHome extends StatelessWidget {
const MobileHome({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Center(
child: Text('Mobile Home'),
),
);
}
}
class TabletHome extends StatelessWidget {
const TabletHome({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Center(
child: Text('Tablet Home'),
),
);
}
}
class DesktopHome extends StatelessWidget {
const DesktopHome({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Center(
child: Text('Desktop Home'),
),
);
}
}
43. Key Widgets for Responsive Flutter UI
| Widget/API | Purpose |
|---|
LayoutBuilder | Respond to the constraints provided by a parent widget. |
MediaQuery.sizeOf | Read the current application window size. |
Expanded | Fill available space in Row or Column. |
Flexible | Allow children to flex within available space. |
Wrap | Move children to additional lines when necessary. |
SafeArea | Protect content from system UI and display cutouts. |
ConstrainedBox | Apply minimum and maximum layout constraints. |
GridView.builder | Create dynamic responsive grids. |
NavigationBar | Compact navigation suitable for smaller layouts. |
NavigationRail | Navigation pattern useful for wider layouts. |
44. Quick Revision
- Responsive UI adjusts the interface according to available space.
- Adaptive UI can change the structure or interaction model according to available space.
LayoutBuilder works with parent constraints.
MediaQuery.sizeOf() provides the overall application window size.
Expanded and Flexible help create flexible layouts.
Wrap helps content move onto additional lines.
SafeArea protects important content from system UI.
- Responsive grids can change their column count.
- Navigation can change between compact and large-screen patterns.
- Large-screen content should often have a maximum width.
- Responsive decisions should generally be based on available space rather than device names.
- Always test layouts at multiple window sizes.
45. Practice Exercises
- Create a responsive profile screen that changes from one column to two columns.
- Create a product grid with 2 columns on small screens, 3 on medium screens, and 4 on large screens.
- Create a responsive dashboard with dynamic cards.
- Create a mobile bottom navigation and desktop navigation rail.
- Create a responsive login form with a maximum width.
- Create a responsive sidebar and content layout.
- Create a responsive image gallery using
GridView.builder.
- Create a responsive settings screen.
- Create a responsive student management dashboard.
- Resize a Flutter web application and verify that the layout adapts correctly.
46. Conclusion
Responsive UI is an essential Flutter skill for building applications that work across different screen sizes and form factors. Flutter provides powerful tools such as LayoutBuilder, MediaQuery.sizeOf(), Expanded, Flexible, Wrap, SafeArea, ConstrainedBox, and responsive navigation widgets.
The most important principle is to design according to the space available to the application, rather than making assumptions about whether the user is holding a phone, tablet, or desktop device. A well-designed responsive Flutter application can reuse the same data and components while changing their arrangement to provide an appropriate experience at different sizes.
Official Flutter Resources
Learn Flutter with JustAcademy